import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs/Observable';

import { map, catchError } from 'rxjs/operators';
import FormatWFS from 'ol/format/wfs';


import { QueryParams } from '../models';

import { Layer } from '../class';
import { WFSOptions, FeaturePropertyType, PropertyTypes } from '../models/layer.model';
import { LocalstoreService } from '../../shared/services';
import { UserToken } from '../../shared/modes';

export interface LayerConfig {
  id: string;
  content: Layer;
  userId: string;
  showByDefault: boolean;
}

@Injectable()
export class LayersService {

  constructor(
    private localstorage: LocalstoreService,
    private http: HttpClient
  ) { }

  getlayer(id: string): Observable<Layer> {
    const userToken: UserToken = this.getUserToken();
    const filter: object = { 
      "where": { 
        "id": id
      } 
    };
    return this.http
      .get(encodeURI(`platform/api/users/${userToken.userId}/layerConfigs?filter=${JSON.stringify(filter)}&access_token=${userToken.id}`))
      .pipe(
        map((data: LayerConfig[]) => {
          return data[0] ? {
            ...data[0].content,
            id: data[0].id,
            default: data[0].showByDefault
          } : null;
        }),
        catchError(error => Observable.throw(error))
      );
  }

  getLayers(ids: string[]): Observable<Layer[]> {
    const userToken: UserToken = this.getUserToken();
    const filter: object = { 
      "where": { 
        "or": ids.map(id => ({ id: id }))
      } 
    };
    return this.http
      .get(encodeURI(`platform/api/users/${userToken.userId}/layerConfigs?filter=${JSON.stringify(filter)}&access_token=${userToken.id}`))
      .pipe(
        map((data: LayerConfig[]) => {
          return data.map((layerConfig: LayerConfig) => {
            return {
              ...layerConfig.content,
              id: layerConfig.id,
              default: layerConfig.showByDefault
            };
          });
        }),
        catchError(error => Observable.throw(error))
      );
  }

  getDefaultlayers(): Observable<Layer[]> {
    const userToken: UserToken = this.getUserToken();
    const filter: object = { 
      "where": { 
        "showByDefault": true 
      } 
    };
    return this.http
      .get(encodeURI(`platform/api/users/${userToken.userId}/layerConfigs?filter=${JSON.stringify(filter)}&access_token=${userToken.id}`))
      .pipe(
        map((data: LayerConfig[]) => {
          return data.map((layerConfig: LayerConfig) => {
            return {
              ...layerConfig.content,
              id: layerConfig.id,
              default: layerConfig.showByDefault
            };
          });
        }),
        catchError(error => Observable.throw(error))
      );
  }

  loadSource(url: string, options: any = {}): Observable<any>  {
    return this.http.get(url, options);
  }

  describeFeatureType(options: WFSOptions): Observable<FeaturePropertyType[]> {
    const url: string = `${options.host}/geoserver/ows?service=wfs&version=2.0.0&request=DescribeFeatureType&typeName=${options.namespace}:${options.name}&outputFormat=application/json`;
    return this.http.get(url).pipe(
      map((data: any) => {
        const properties: any[] = data.featureTypes[0].properties;
        return properties
          .filter(property => property.name !== options.geometryName)
          .map(property => {
            return {
              name: property.name,
              alias: property.name,
              type: this.serializePropertyType(property.localType)
            };
          });
      }),
      catchError(error => Observable.throw(error))
    );
  }

  query(url: string, params: QueryParams): Observable<any> {
    const featureRequest = (new FormatWFS()).writeGetFeature(params);
    const data = new XMLSerializer().serializeToString(featureRequest); 
    return this.http.post(url, data).pipe(
      map(data => data),
      catchError(error => Observable.throw(error))
    );
  }

  private serializePropertyType(type: string): string {
    switch(type) {
      case 'int':
      case 'number': {
        return PropertyTypes.Number;
      }
      case 'date-time': {
        return PropertyTypes.DateTime;
      }
      default: {
        return PropertyTypes.String;
      }
    }
  }

  private getUserToken(): UserToken {
    const userToken: UserToken = this.localstorage.getItem('USER_TOKEN');
    return userToken;
  }

}
